package cn.sunxiansheng.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置类，会将 Object 类型序列化为 JSON 格式，然后将其作为字符串存储在 Redis 中。
 * 当从 Redis 中读取时，会将 JSON 字符串反序列化回具体的 Object 类型数据，直接强制转换即可，无需手动解析 JSON。
 */
@Configuration
public class RedisConfig {

    /**
     * 自定义RedisTemplate Bean，配置Key和Value的序列化方式。
     * Key采用String序列化，Value采用JSON序列化，支持对象直接转为JSON格式存储在Redis中。
     *
     * @param redisConnectionFactory Redis连接工厂，用于创建RedisTemplate连接
     * @return 配置完成的RedisTemplate实例，用于Redis数据操作
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 设置Key的序列化方式为StringRedisSerializer，以便Key以字符串格式存储，便于人类读取
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);          // 序列化Redis键
        redisTemplate.setHashKeySerializer(stringSerializer);      // 序列化Hash结构的键

        // 设置Value和HashValue的序列化方式为Jackson2JsonRedisSerializer，以便Value以JSON格式存储
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = jackson2JsonRedisSerializer();
        redisTemplate.setValueSerializer(jacksonSerializer);       // 序列化Redis值
        redisTemplate.setHashValueSerializer(jacksonSerializer);   // 序列化Hash结构的值

        // 初始化设置的配置
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 配置Jackson序列化器，以支持复杂Java对象的序列化和反序列化。
     * 使用Jackson2JsonRedisSerializer将对象序列化为JSON字符串，从而提高Redis中的可读性和兼容性。
     *
     * @return 配置好的Jackson2JsonRedisSerializer实例，指定序列化格式为JSON
     */
    private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
        // 创建Jackson2JsonRedisSerializer，指定泛型为Object，支持序列化任意Java对象
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        // 创建ObjectMapper用于配置JSON序列化方式
        ObjectMapper objectMapper = new ObjectMapper();

        // 设置可见性，允许访问所有字段（包括私有和保护字段），确保所有属性都被序列化和反序列化
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        // 配置反序列化时忽略未知属性，防止因JSON中多余字段导致反序列化错误
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 序列化时忽略空值属性，节省存储空间
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // 启用类型信息，确保反序列化时能够恢复原始对象类型
        objectMapper.activateDefaultTyping(
                BasicPolymorphicTypeValidator.builder()
                        .allowIfSubType(Object.class)
                        .build(),
                ObjectMapper.DefaultTyping.NON_FINAL
        );

        // 将配置好的ObjectMapper设置到Jackson2JsonRedisSerializer中，应用上述配置
        jacksonSerializer.setObjectMapper(objectMapper);
        return jacksonSerializer;
    }
}